Skip to content

Conversation

@jiexi
Copy link
Contributor

@jiexi jiexi commented Feb 2, 2026

Explanation

Improves reliability of wallet_sendCalls.

Changes getAccounts hook to getPermittedAccountsForOrigin hook which takes no params and returns the permitted accounts for the origin of the request

References

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Note

Medium Risk
Changes the public hook surface from getAccounts(req) to getPermittedAccountsForOrigin() and makes origin mandatory for wallet_sendCalls/wallet_getCapabilities, which may require consumer updates. Also changes wallet_sendCalls default from selection and unauthorized error behavior when no permitted accounts exist.

Overview
Improves wallet_sendCalls reliability by switching account authorization to an origin-scoped getPermittedAccountsForOrigin() hook and using its first returned account as the default from when the request omits from (throwing providerErrors.unauthorized() if none are permitted).

Updates wallet_sendCalls, wallet_getCapabilities, and validateAndNormalizeKeyholder to use the new hook signature and require origin on requests, and adjusts/extends tests plus the package changelog accordingly.

Written by Cursor Bugbot for commit f77bd57. This will update automatically on new commits. Configure here.

@jiexi jiexi requested a review from a team as a code owner February 2, 2026 22:14
@jiexi jiexi changed the title jl/make wallet sendCalls better feat: make wallet_sendCalls more reliable Feb 2, 2026
@jiexi jiexi requested a review from a team as a code owner February 2, 2026 22:20
address: Hex,
req: JsonRpcRequest,
{ getAccounts }: { getAccounts: (req: JsonRpcRequest) => Promise<string[]> },
{ getPermittedAccountsForOrigin }: { getPermittedAccountsForOrigin: () => Promise<string[]> },
Copy link
Member

@matthewwalsh0 matthewwalsh0 Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we avoid breaking changes for all clients if we kept the current name, but just ensured the hooks check the origin?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah Jiexi and I went back and forth on this a bit, but I think for this case the name change is worth it. The bug this PR fixes happened precisely because getAccounts is ambiguous - it doesn't convey that these must be permitted accounts for the requesting origin. A future developer could reasonably implement getAccounts by returning all accounts or the selected account, not realizing this breaks the security model.
Since this is already a breaking change (the signature changed from (req) => Promise<string[]> to () => Promise<string[]>), we'd prefer to make the API self-documenting and reduce the risk of similar bugs.

// Ensure that an "unauthorized" error is thrown if the requester
// does not have the `eth_accounts` permission.
const accounts = await getAccounts(req);
const accounts = await getPermittedAccountsForOrigin();
Copy link
Member

@matthewwalsh0 matthewwalsh0 Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does the client access the origin if we don't provide anything to the hook?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hook gets the origin off the request object directly. The 7702 middleware follows the same pattern that I'm hoping to use here

matthewwalsh0
matthewwalsh0 previously approved these changes Feb 3, 2026
adonesky1
adonesky1 previously approved these changes Feb 3, 2026
Copy link
Contributor

@adonesky1 adonesky1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@jiexi jiexi dismissed stale reviews from adonesky1 and matthewwalsh0 via 8f310db February 3, 2026 21:56
matthewwalsh0
matthewwalsh0 previously approved these changes Feb 4, 2026
Copy link
Contributor

@adonesky1 adonesky1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.


if (!from) {
throw providerErrors.unauthorized();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unneeded accounts lookup can break sendCalls

Medium Severity

processSendCalls now always awaits getPermittedAccountsForOrigin() even when params.from is provided. This introduces a new failure point and latency for requests that previously did not need any accounts lookup, so transient hook errors can now reject otherwise-valid wallet_sendCalls requests.

Fix in Cursor Fix in Web

// The first account returned by `getPermittedAccountsForOrigin` is the selected account for the origin
const [selectedAccount] = await getPermittedAccountsForOrigin();
const from = paramFrom ?? selectedAccount;

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sender selection relies on account ordering

High Severity

When from is omitted, processSendCalls uses the first element returned by getPermittedAccountsForOrigin() as the selected account. The hook’s contract only says “permitted accounts”, so if ordering is not guaranteed, the chosen sender can be the wrong permitted account.

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants